package cn.iocoder.yudao.module.ai.service.modelfactory;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.ai.controller.admin.busimodel.vo.BusiModelAndParamVO;
import cn.iocoder.yudao.module.ai.controller.admin.modelfactoryparam.vo.ModelFactoryAndParamVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.busimodelparam.BusiModelParamDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.modelfactoryparam.ModelFactoryParamDO;
import cn.iocoder.yudao.module.ai.dal.mysql.modelfactoryparam.ModelFactoryParamMapper;
import cn.iocoder.yudao.module.ai.service.busimodel.BusiModelService;
import cn.iocoder.yudao.module.ai.service.jupyter.JupyterService;
import cn.iocoder.yudao.module.ai.utils.UnZipUtils;
import cn.iocoder.yudao.module.infra.util.DateUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import cn.iocoder.yudao.module.ai.controller.admin.modelfactory.vo.*;
import cn.iocoder.yudao.module.ai.dal.dataobject.modelfactory.ModelFactoryDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.ai.dal.mysql.modelfactory.ModelFactoryMapper;


/**
 * 模型车间 Service 实现类
 *
 * @author gt
 */
@Service
@Validated
@Slf4j
public class ModelFactoryServiceImpl implements ModelFactoryService {
    @Resource
    private BusiModelService busiModelService;
    @Resource
    private JupyterService jupyterService;
    @Value("${modelFactory.build-path}")
    private String buildPath;
    //公共上传地址
    @Value("${yudao.file.upload-path}")
    private String commonPath;
    @Value("${jupyter.env-file-name}")
    private String envFile;

    @Value("${jupyter.config-file-name}")
    private String sceneFile;

    @Resource
    private ModelFactoryMapper modelFactoryMapper;
    @Resource
    private ModelFactoryParamMapper paramMapper;
    @Resource
    private ModelFactoryParamMapper modelFactoryParamMapper;



    @Override
    public Long createModelFactory(ModelFactorySaveReqVO createReqVO) {
        // 插入
        ModelFactoryDO modelFactory = BeanUtils.toBean(createReqVO, ModelFactoryDO.class);
        modelFactoryMapper.insert(modelFactory);
        // 返回
        return modelFactory.getId();
    }

    @Override
    public void updateModelFactory(ModelFactorySaveReqVO updateReqVO) {
        // 更新
        ModelFactoryDO updateObj = BeanUtils.toBean(updateReqVO, ModelFactoryDO.class);
        modelFactoryMapper.updateById(updateObj);
        List<ModelFactoryParamDO> paramDOList =  updateReqVO.getModelFactoryParamDOList();
        if(ObjectUtil.isNotEmpty(paramDOList)) {
            paramDOList.forEach(modelParamDO -> modelParamDO.setBusiModelId(updateReqVO.getId().intValue()));
        }
        //删除旧的参数
        List<ModelFactoryParamDO> deleteDOList = paramMapper.selectList(ModelFactoryParamDO::getBusiModelId,updateReqVO.getId());
        if(CollectionUtil.isNotEmpty(deleteDOList))
            paramMapper.deleteBatchIds(deleteDOList);
        //更新参数
        if(CollectionUtil.isNotEmpty(paramDOList)) {
            paramDOList = paramDOList.stream().map(obj ->
            {
                obj.setId(null);
                obj.setBusiModelId(updateReqVO.getId().intValue());
                return obj;
            }).collect(Collectors.toList());
            paramMapper.insertBatch(paramDOList);
        }
    }

    @Override
    public void deleteModelFactory(Long id) {
        paramMapper.delete(ModelFactoryParamDO::getBusiModelId,id);
        // 删除
        modelFactoryMapper.deleteById(id);
    }



    @Override
    public ModelFactoryAndParamVO getModelFactory(Long id) {
        ModelFactoryDO modelFactoryDO = modelFactoryMapper.selectById(id);
        List<ModelFactoryParamDO> params = modelFactoryParamMapper.selectList(ModelFactoryParamDO::getBusiModelId,id);
        ModelFactoryAndParamVO modelAndParamVO = BeanUtils.toBean(modelFactoryDO, ModelFactoryAndParamVO.class);
        modelAndParamVO.setParams(params);
        return modelAndParamVO;

    }

    @Override
    public PageResult<ModelFactoryDO> getModelFactoryPage(ModelFactoryPageReqVO pageReqVO) {
        return modelFactoryMapper.selectPage(pageReqVO);
    }

    @Override
    public String buildModel(ModelFactoryDO modelFactoryDO) throws IOException {
        log.info("开始构建 {} 模型,模型id: {}",modelFactoryDO.getModelChinessName(),modelFactoryDO.getId());
        modelFactoryDO = this.getModelFactory(modelFactoryDO.getId());
        modelFactoryDO.getJupyterId();
        //获取业务模型路径
        String busiZipPath = busiModelService.buildBusinessModel(null, jupyterService.getJupyter(modelFactoryDO.getJupyterId().intValue()));
        log.info("获取到业务模型路径:{}",busiZipPath);
        //获取识别模型路径
        String modelPath = commonPath + modelFactoryDO.getModelZipPath().split("file/")[1];
        log.info("获取到识别模型路径:{}",modelPath);
        String distPath = DateUtils.datePath()+"/" + modelFactoryDO.getModelName() + "_" + new Date().getTime();
         handleZips(busiZipPath,modelPath,commonPath+ distPath,modelFactoryDO.getId().intValue());
        return distPath;
    }


    private void buildEnvFile(Integer modelId, Path envPath) {
        List<ModelFactoryParamDO> envParams =  modelFactoryParamMapper.selectParams(modelId,"evnParam");
        Map<String, String> paramMap = envParams.stream()
                .collect(Collectors.toMap(
                        ModelFactoryParamDO::getParamName,
                        ModelFactoryParamDO::getDefaultValue
                ));
        try {
            Files.write(envPath, paramMap.entrySet().stream()
                            .map(entry -> entry.getKey() + "=" + entry.getValue())
                            .collect(Collectors.joining("\n"))
                            .getBytes(),
                    StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);

            log.info("Map 被写入到文件: " + envPath);
        } catch (IOException e) {
            System.err.println("写入文件失败: " + e.getMessage());
            e.printStackTrace();
        }

    }

    private void buildSceneFile(Integer modelId, Path scenePath) {
        List<ModelFactoryParamDO> envParams =  modelFactoryParamMapper.selectParams(modelId,"scenePath");

        Map<String, String> paramMap = envParams.stream()
                .collect(Collectors.toMap(
                        ModelFactoryParamDO::getParamName,
                        ModelFactoryParamDO::getDefaultValue
                ));
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String jsonString = objectMapper.writeValueAsString(paramMap);
            Files.write(scenePath, jsonString.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);

            log.info("JSON 被写入到文件: " + scenePath);
        } catch (IOException e) {
            log.info("写入文件失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    public String handleZips(String busiZipPath, String modelPath, String buildPath,Integer modelId) throws IOException {
        // Step 1: 解压 modelPath 到 buildPath
        Path buildDirPath = Paths.get(buildPath);
        Files.createDirectories(buildDirPath);
        unzip(modelPath, buildDirPath);

        // Step 2: 解压 busiZipPath 到 buildPath/__pycache__
        Path pycacheDirPath = buildDirPath.resolve("__pycache__");
        Files.createDirectories(pycacheDirPath);
        unzip(busiZipPath, pycacheDirPath);

        // Step 3: 填充scene.conf和evn.sh
        Path evnPath = Paths.get(buildPath +"/" + envFile);
        buildEnvFile(modelId,evnPath);
        Path scenePath = Paths.get(buildPath +"/" + sceneFile);
        buildSceneFile(modelId,scenePath);

        // Step 3: 将整个文件夹压缩成 zip 包
        String finalZipPath = buildPath + ".zip";
        UnZipUtils.zipDirectory(buildDirPath.toAbsolutePath().toString(), finalZipPath);

        return finalZipPath;
    }

    private static void unzip(String zipFilePath, Path destDir) throws IOException {
        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath))) {
            ZipEntry zipEntry = zis.getNextEntry();
            while (zipEntry != null) {
                Path newFilePath = destDir.resolve(zipEntry.getName());
                if (zipEntry.isDirectory()) {
                    Files.createDirectories(newFilePath);
                } else {
                    new File(newFilePath.getParent().toString()).mkdirs();
                    try (BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(newFilePath))) {
                        byte[] bytesIn = new byte[4096];
                        int read;
                        while ((read = zis.read(bytesIn)) != -1) {
                            bos.write(bytesIn, 0, read);
                        }
                    }
                }
                zipEntry = zis.getNextEntry();
            }
            zis.closeEntry();
        }
    }






}